{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 0.导入库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 加入下面2行，可以使py代码文件中的修改即时生效\n",
    "%load_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 如果python2使用print，也得加上括号\n",
    "from __future__ import print_function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\h5py\\__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n",
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "# 导入keras\n",
    "import keras"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的这4种层：全连接层Dense，2维卷积层Conv2D，批量归一化层BatchNormalization，激活层Activation\n",
    "from keras.layers import Dense, Conv2D, BatchNormalization, Activation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的这3种层：平均2维池化层AveratePooling2D, 输入层Input，激活层Activation\n",
    "from keras.layers import AveragePooling2D, Input, Flatten"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的优化器：Adam优化器\n",
    "from keras.optimizers import Adam"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的回传函数：模型检查点ModelCheckpoint, LearningRateScheduler\n",
    "from keras.callbacks import ModelCheckpoint, LearningRateScheduler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的回传函数：学习率递减ReduceLROnPlateau\n",
    "from keras.callbacks import ReduceLROnPlateau"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的图片处理函数：图片数据生成器\n",
    "from keras.preprocessing.image import ImageDataGenerator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的正则化函数：L2正则化\n",
    "from keras.regularizers import l2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的后端：backend中文叫做后端，取别名为K\n",
    "from keras import backend as K"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的模型函数：Model\n",
    "from keras.models import Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入keras库的数据集类：cifar10\n",
    "from keras.datasets import cifar10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入必需的常用库\n",
    "import numpy as np\n",
    "import os"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.数据准备"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1 加载训练集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_X矩阵转置前： (50000, 3, 32, 32)\n",
      "train_X矩阵转置后： (50000, 32, 32, 3)\n"
     ]
    }
   ],
   "source": [
    "## 加载数据集cifar10里面的训练集\n",
    "from keras.datasets.cifar import load_batch\n",
    "\n",
    "def load_train_dataset(dirPath='../resources/cifar-10-batches-py/'):\n",
    "    train_sample_quantity = 50000\n",
    "    image_width = 32\n",
    "    image_height = 32\n",
    "    channel_quantity = 3\n",
    "    train_X = np.zeros((train_sample_quantity, channel_quantity, image_width, image_height),\n",
    "                       dtype='uint8')\n",
    "    train_y = np.zeros((train_sample_quantity, ),\n",
    "                       dtype='uint8')\n",
    "    for i in range(1, 6):\n",
    "        fileName = 'data_batch_%d' %i\n",
    "        filePath = os.path.join(dirPath, fileName)\n",
    "        startIndex = (i - 1) * 10000\n",
    "        endIndex = i * 10000\n",
    "        train_X[startIndex:endIndex, :, :, :], train_y[startIndex:endIndex] = load_batch(filePath)\n",
    "    print('train_X矩阵转置前：', train_X.shape)\n",
    "    # 从官网上下载的数据集的4个维度为样本个数n、通道数c、宽度w、高度h\n",
    "    # Keras基于Tensorflow，数据的维度顺序要求：样本个数n、宽度w、高度h、通道数c，所以使用np.transpose完成矩阵转置\n",
    "    train_X = train_X.transpose(0, 2, 3, 1)\n",
    "    print('train_X矩阵转置后：', train_X.shape)\n",
    "    return train_X, train_y\n",
    "\n",
    "dirPath = '../resources/cifar-10-batches-py/'\n",
    "train_X, train_y = load_train_dataset() "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 加载测试集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test_X矩阵转置前： (10000, 3, 32, 32)\n",
      "test_X矩阵转置后： (10000, 32, 32, 3)\n"
     ]
    }
   ],
   "source": [
    "# 加载数据集cifar10里面的测试集\n",
    "def load_test_dataset(dirPath='../resources/cifar-10-batches-py/'):\n",
    "    fileName = 'test_batch'\n",
    "    filePath = os.path.join(dirPath, fileName)\n",
    "    test_X, test_y = load_batch(filePath)\n",
    "    print('test_X矩阵转置前：', test_X.shape)\n",
    "    test_X = test_X.transpose(0, 2, 3, 1)\n",
    "    print('test_X矩阵转置后：', test_X.shape)\n",
    "    return test_X, test_y\n",
    "\n",
    "dirPath = '../resources/cifar-10-batches-py/'\n",
    "test_X, test_y = load_test_dataset() "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3 对类别做One-Hot编码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 对类别ID做One-Hot编码\n",
    "from keras.utils import to_categorical\n",
    "\n",
    "class_quantity = 10\n",
    "train_Y = to_categorical(train_y, class_quantity)\n",
    "test_Y = to_categorical(test_y, class_quantity)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 设定学习率\n",
    "def get_learningRate(epoch):\n",
    "    learningRate = 1e-3\n",
    "    if epoch > 180:\n",
    "        learningRate *= 0.5e-3\n",
    "    elif epoch > 160:\n",
    "        learningRate *= 1e-3\n",
    "    elif epoch > 120:\n",
    "        learningRate *= 1e-2\n",
    "    elif epoch > 80:\n",
    "        learningRate *= 1e-1\n",
    "    print('learning rate:', learningRate)\n",
    "    return learningRate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义resnet层\n",
    "def resnet_layer(inputs,\n",
    "        filter_quantity=16,\n",
    "        filter_size=3,\n",
    "        strides=1,\n",
    "        activation='relu',\n",
    "        batch_normalization=True,\n",
    "        conv_first=True):\n",
    "    layer_convolution = Conv2D(filter_quantity,\n",
    "        kernel_size=filter_size,\n",
    "        strides=strides,\n",
    "        padding='same',\n",
    "        kernel_initializer='he_normal',\n",
    "        kernel_regularizer=l2(1e-4))\n",
    "    x = inputs\n",
    "    if conv_first:\n",
    "        x = conv(x)\n",
    "        if batch_normalization:\n",
    "            x = BatchNormalization()(x)\n",
    "        if activation is not None:\n",
    "            x = Activation(activation)(x)\n",
    "    else:\n",
    "        if batch_normalization:\n",
    "            x = BatchNormalization()(x)\n",
    "        if activation is not None:\n",
    "            x = Activation(activation)(x)\n",
    "        x = conv(x)\n",
    "    return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 获取resnet模型\n",
    "def get_resnetModel_v2(input_shape, depth, class_quantity=10):\n",
    "    if (depth - 2) % 9 !=0:\n",
    "        raise ValueError('depth should be 9n+2(eg 56 or 110)')\n",
    "    in_filter_quantity = 16\n",
    "    res_block_quantity = 16\n",
    "    inputs = Input(shape=input_shape)\n",
    "    x = resnet_layer(inputs=inputs,\n",
    "        filter_quantity=in_filter_quantity,\n",
    "        conv_first=True)\n",
    "    for stage in range(3):\n",
    "        for res_block in range(res_block_quantity):\n",
    "            activation = 'relu'\n",
    "            batch_normalization = True\n",
    "            strides =1\n",
    "            if stage == 0:\n",
    "                out_filter_quantity = in_filter_quantity * 4\n",
    "                if res_block == 0:\n",
    "                    activation=None\n",
    "                    batch_normalization = False\n",
    "            else:\n",
    "                out_filter_quantity = in_filter_quantity * 2\n",
    "                if res_block == 0:\n",
    "                    strides = 2\n",
    "            y = resnet_layer(inputs=x,\n",
    "                filter_quantity=in_filter_quantity,\n",
    "                filter_size=1,\n",
    "                strides=strides,\n",
    "                activation=activation,\n",
    "                batch_normalization=batch_normalization,\n",
    "                conv_first=False)\n",
    "            y = resnet_layer(inputs=y,\n",
    "                filter_quantity=in_filter_quantity,\n",
    "                conv_first=False)\n",
    "            y = resnet_layer(inputs=y,\n",
    "                filter_quantity=out_filter_quantity,\n",
    "                filter_size=1,\n",
    "                conv_first=False)\n",
    "            if res_block == 0:\n",
    "                x = resnet_layer(inputs=x,\n",
    "                    filter_quantity=out_filter_quantity,\n",
    "                    filter_size=1,\n",
    "                    strides=strides,\n",
    "                    activation=None,\n",
    "                    batch_normalization=False)\n",
    "            x = keras.layers.add([x, y])\n",
    "        in_filter_quantity = out_filter_quantity\n",
    "    x = BatchNormalization()(x)\n",
    "    x = Activation('relu')(x)\n",
    "    x = AveragePooling2D(pool_size=8)(x)\n",
    "    x = Flatten(x)\n",
    "    outputs = Dense(num_classes,\n",
    "        activation='softmax',\n",
    "        kernel_initializer='he_normal')(x)\n",
    "    model = Model(inputs=inputs, outputs=outputs)\n",
    "    return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'conv' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-24-f75b622acc70>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0minput_shape\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mtrain_x\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m resnetModel_v2 = get_resnetModel_v2(input_shape=input_shape,\n\u001b[1;32m----> 3\u001b[1;33m     depth=depth)\n\u001b[0m\u001b[0;32m      4\u001b[0m resnetModel_v2.compile(loss='categorical_crossentropy',\n\u001b[0;32m      5\u001b[0m     \u001b[0moptimizer\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mAdam\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mget_learningRate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-21-498e66a3fa76>\u001b[0m in \u001b[0;36mget_resnetModel_v2\u001b[1;34m(input_shape, depth, class_quantity)\u001b[0m\n\u001b[0;32m      8\u001b[0m     x = resnet_layer(inputs=inputs,\n\u001b[0;32m      9\u001b[0m         \u001b[0mfilter_quantity\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0min_filter_quantity\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 10\u001b[1;33m         conv_first=True)\n\u001b[0m\u001b[0;32m     11\u001b[0m     \u001b[1;32mfor\u001b[0m \u001b[0mstage\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     12\u001b[0m         \u001b[1;32mfor\u001b[0m \u001b[0mres_block\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mres_block_quantity\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-20-c0b7f61ce5df>\u001b[0m in \u001b[0;36mresnet_layer\u001b[1;34m(inputs, filter_quantity, filter_size, strides, activation, batch_normalization, conv_first)\u001b[0m\n\u001b[0;32m     15\u001b[0m     \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0minputs\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     16\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[0mconv_first\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 17\u001b[1;33m         \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mconv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     18\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0mbatch_normalization\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     19\u001b[0m             \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mBatchNormalization\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mNameError\u001b[0m: name 'conv' is not defined"
     ]
    }
   ],
   "source": [
    "input_shape = train_x.shape[1:]\n",
    "resnetModel_v2 = get_resnetModel_v2(input_shape=input_shape,\n",
    "    depth=depth)\n",
    "resnetModel_v2.compile(loss='categorical_crossentropy',\n",
    "    optimizer=Adam(lr=get_learningRate(0)),\n",
    "    metrics=['accuracty'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
